package data

import (
	conf "medicine/internal/conf"

	"context"

	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-redis/redis/v8"
	"github.com/google/wire"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"

	"medicine/internal/biz"
)

// ProviderSet is data providers.
var ProviderSet = wire.NewSet(
	NewData,
	NewGreeterRepo,
	NewProductRepo,
	NewSeckillProductRepo,
	NewSeckillEventRepo,
	NewCategoryRepo,
	NewBrandRepo,
	NewSeckillOrderRepo, // 注入秒杀订单Repo
	NewUserRepo,         // 注入用户Repo
	NewPointsRepo,       // 注入积分Repo
)

// Data 数据层核心结构体，包含数据库和缓存客户端
type DataRepo struct {
	DB     *gorm.DB       // 数据库客户端
	Redis  *redis.Client  // Redis 客户端
	MQ     *MQClient      // 新增RabbitMQ客户端
	Alipay *AlipayService // 支付宝服务
}

// 全局变量，便于 main.go 定时任务调用
var GlobalSeckillProductRepo biz.SeckillProductRepo
var GlobalDataRepo *DataRepo

// NewData .
func NewData(c *conf.Data, logger log.Logger) (*DataRepo, func(), error) {
	// 初始化 MySQL
	db, err := gorm.Open(mysql.Open(c.Database.Source), &gorm.Config{})
	if err != nil {
		return nil, nil, err
	}

	// 自动迁移建表
	if err := db.AutoMigrate(
		&biz.Product{},
		&biz.SeckillProduct{},
		&biz.SeckillEvent{},
		&biz.Category{},
		&biz.Brand{},
		&biz.SeckillOrder{}, // 自动迁移秒杀订单表
		&biz.User{},         // 自动迁移用户表
		&biz.PointsRecord{}, // 自动迁移积分记录表
		&biz.PointsRule{},   // 自动迁移积分规则表
	); err != nil {
		return nil, nil, err
	}

	// 初始化 Redis
	rdb := redis.NewClient(&redis.Options{
		Addr:         c.Redis.Addr,
		Password:     c.Redis.Password, // 添加密码支持
		ReadTimeout:  c.Redis.ReadTimeout.AsDuration(),
		WriteTimeout: c.Redis.WriteTimeout.AsDuration(),
	})

	// 修复：c.MQ 未定义，需从配置中正确获取 MQ 地址
	// 假设配置结构体为 c.Mq.Addr，如果没有则需在 conf.Data 中添加 MQ 字段
	mq, err := NewMQClient(c.Mq.Addr) // 需在 config.yaml 配置 MQ 地址
	if err != nil {
		return nil, nil, err
	}

	// 初始化支付宝服务
	alipayService, err := NewAlipayService(c.Alipay)
	if err != nil {
		return nil, nil, err
	}

	cleanup := func() {
		log.NewHelper(logger).Info("closing the data resources")
		sqlDB, _ := db.DB()
		sqlDB.Close()
		rdb.Close()
	}
	data := &DataRepo{
		DB:     db,
		Redis:  rdb,
		MQ:     mq,
		Alipay: alipayService,
	}
	// 初始化 repo
	GlobalSeckillProductRepo = NewSeckillProductRepo(data)
	GlobalDataRepo = data
	return data, cleanup, nil
}

// UpdateOrderStatus 更新订单状态（通过 seckillOrderRepo 实现，供支付宝回调等场景调用）
func (d *DataRepo) UpdateOrderStatus(ctx context.Context, orderId string, status string) error {
	repo := NewSeckillOrderRepo(d)
	if r, ok := repo.(*seckillOrderRepo); ok {
		return r.UpdateOrderStatus(ctx, orderId, status)
	}
	return nil
}

// UpdateOrderStatusByID 根据订单ID更新订单状态（供定时任务使用）
func (d *DataRepo) UpdateOrderStatusByID(ctx context.Context, orderId int64, status string) error {
	return d.DB.WithContext(ctx).Model(&biz.SeckillOrder{}).
		Where("id = ?", orderId).
		Update("status", status).Error
}

// RollbackStock 回滚订单对应的 Redis 虚拟库存（通过 seckillOrderRepo 实现，供超时订单自动取消等场景调用）
func (d *DataRepo) RollbackStock(ctx context.Context, orderId int64) error {
	repo := NewSeckillOrderRepo(d)
	if r, ok := repo.(*seckillOrderRepo); ok {
		return r.RollbackStock(ctx, orderId)
	}
	return nil
}

// RollbackStockForce 强制回滚订单对应的 Redis 虚拟库存（不检查订单状态，用于定时任务等场景）
func (d *DataRepo) RollbackStockForce(ctx context.Context, orderId int64) error {
	repo := NewSeckillOrderRepo(d)
	if r, ok := repo.(*seckillOrderRepo); ok {
		return r.RollbackStockForce(ctx, orderId)
	}
	return nil
}
